use std::future::Future;
use std::time::Duration;
use elasticsearch::Error;
use elasticsearch::http::response::Response;
use moka::future::Cache;
use serde_json::Value;
use tokio::sync::OnceCell;

pub(crate) mod index;
pub(crate) mod client;
pub(crate) mod search;

static CACHE: OnceCell<Cache<String, Value>> = OnceCell::const_new();

pub async fn init_cache() -> &'static Cache<String, Value> {
    CACHE.get_or_init(|| async {
        let cache: Cache<String, Value> = Cache::builder()
            .time_to_live(Duration::from_secs(24 * 60 * 60))
            .build();
        cache
    }).await
}

pub async fn cache_insert_map<F, Fut>(key: String, refresh: bool, f: F) -> Value
    where
        F: FnOnce() -> Fut,
        Fut: Future<Output = Option<Value>>,
{
    let cache = cache().await;
    let values_option = cache.get(&key).await;
    if refresh || values_option.is_none() {
        let values = f().await;
        match values {
            None => {
                Value::Null
            }
            Some(result) => {
                cache.insert(key.clone(), result.clone()).await;
                result
            }
        }
    }else {
        cache_get(key.clone()).await.unwrap()
    }
}

pub async fn cache_get(key: String) -> Option<Value> {
    cache().await.get(&key).await
}

pub async fn cache() ->  &'static Cache<String, Value> {
    init_cache().await
}


pub async fn get_values(result :Result<Response, Error>) -> Option<Value> {
    match result {
        Ok(resp) => {
            let result_values = resp.json::<Value>().await;
            match result_values {
                Ok(values) => {
                    Some(values)
                }
                Err(err) => {
                    println!("{:?}", err);
                    None
                }
            }
        }
        Err(err) => {
            println!("{:?}", err);
            None
        }
    }
}